/******************************************************************************
 * Student Name: Ofri Keidar
 * Exercise Name: Ex2
 * File Description: Class StudentLinkedList's implementation
 ******************************************************************************/

#include "StudentLinkedList.h"
#define INDEX_INIT_VAL 0 // used to initialize new nodes' index

//////////////////////////////////////////////////////////////////////////////////// class Node's methods

//////////////////////////////////////////////////////////////////// public methods

/***************************************************************************************
* function name: Node (Student* const studentToPoint)
* The Input: Student data to point at
* The output: None
* The Function operation: Initializes student data pointer to point at given student.
*						  Previous and next node pointers are initialized as NULL and
*						  index is initialized as INDEX_INIT_VAL becuase the new node's
*						  poisiton in the linked list is not set yet
***************************************************************************************/
StudentLinkedList::Node::Node(Student* studentToPoint) {

	m_studentData = studentToPoint;
	// new node is not yet linked to the list
	m_prevNode = NULL;
	m_nextNode = NULL;
	m_index = INDEX_INIT_VAL;
}

/****************************************************************************************
* function name: ~Node()
* The Input: None
* The output: None
* The Function operation: Deletes student data being pointed by the node.
*			   			  Next and previous nodes are linked and the next nodes'
*			              indices are decremented
***************************************************************************************/
StudentLinkedList::Node::~Node() {

	delete m_studentData; // delete allocated memory for pointed student

	// link next node and previous node
	if (m_nextNode != NULL) { // not end of list
		m_nextNode->m_prevNode = m_prevNode;
	}
	if (m_prevNode != NULL) { // not beginning of list
		m_prevNode->m_nextNode = m_nextNode;
	}

	// decrement next nodes' index
	Node* currentNode = m_nextNode;
	while (currentNode != NULL) { // scan list to its end
		--(currentNode->m_index); // decrement index
		currentNode = currentNode->m_nextNode; // move to next node
	}

}

//////////////////////////////////////////////////////////////////////////////////// class StudentLinkedList's methods

//////////////////////////////////////////////////////////////////// private methods

/***************************************************************************************
* function name: findPlace(const Student& newStudent)
* The Input: Pointer to student to add
* The output: Node pointer to which the new student should be linked.
* 	          If a student with new student's id already exists, NULL is returned
* The Function operation: Uses 2 node pointers- one to point at previous node being
*			              scanned and another one to point at current node on list.
*	                      Method compares current sutdent's id with given student's id.
*			              If id's are equal, given student cannot be added and NULL is
*			              returned. If current student's id is smaller than new student's,
*		                  a pointer to current node is returned
***************************************************************************************/
StudentLinkedList::Node* StudentLinkedList::findPlace(const Student& newStudent) const {

	Node* after = m_headDummy; // pointer to previous node being scanned
	Node* curr = after->m_nextNode; // pointer to current node being scanned

	while (after->m_nextNode->m_studentData != NULL) {

		if (curr->m_studentData->getID() == newStudent.getID()) {
			return NULL; //duplicate id found
		}

		if (newStudent.getID() < curr->m_studentData->getID()) {
			return curr;
		}
		after = curr;
		curr = curr->m_nextNode; // move to next node
	}

	return curr;
}

/***************************************************************************************
* function name: internalAdd(Student& newStudent, Node* before)
* The Input: Pointer to student to be added and a pointer to the node where the new
* 	         student will be linked
* The output: None
* The Function operation: A new node is created pointing to student to be added.
*			              Method links the new node before the given node and then
*			              increments next nodes' indices
***************************************************************************************/
void StudentLinkedList::internalAdd(Student& newStudent, Node* before) {

	Node* newNode = new Node(&newStudent); // create a node pointing to new student
	// link node to list
	Node* temp = before->m_prevNode;
	newNode->m_nextNode = before;
	before->m_prevNode = newNode;

	newNode->m_prevNode = temp;
	temp->m_nextNode = newNode;

	++(m_tailDummy->m_index); // increment tail's index
	// update new node's index
	if (before == m_tailDummy) {
		newNode->setIndex(m_tailDummy->m_index);
	} else {
		newNode->setIndex((before->m_index));
	}
	// increment index of next nodes
	while (before != m_tailDummy) {
		before->setIndex((before->m_index)+1);
		before = before->m_nextNode;
	}
}

//////////////////////////////////////////////////////////////////// public methods

/***************************************************************************************
* function name: StudentLinkedList()
* The Input: None
* The output: None
* The Function operation: List's head and tail are pointers to nodes which do not point
*			              at students (point NULL). This implementation prevents the
*			              need to check if previous/next nodes are NULL- makes code
*		                  more quickly because there are less conditions to check
***************************************************************************************/
StudentLinkedList::StudentLinkedList() {

	// create dummy nodes to avoid checking NULL values
	m_headDummy = new Node(NULL);
	m_tailDummy = new Node(NULL);

	m_headDummy->m_nextNode = m_tailDummy;
	m_tailDummy->m_prevNode = m_headDummy;
	m_currentNode = m_headDummy;
}

/***************************************************************************************
* function name: ~StudentLinkedList()
* The Input: None
* The output: None
* The Function operation: Scans list from start to end and deletes each node
***************************************************************************************/
StudentLinkedList::~StudentLinkedList() {

	// scan list and delete nodes
	Node* currNode = m_headDummy;
	Node* prevNode = m_headDummy;

	while (currNode != NULL) {
		// move to next node on list and delete the previous which has been scanned
		currNode = currNode->m_nextNode;
		delete prevNode;
		prevNode = currNode;
	}
	delete prevNode; // delete last node on list
}

/***************************************************************************************
* function name: addNode(Student* newStudent)
* The Input: Pointer to student to be added
* The output: True if student was added, False is not
* The Function operation: If NULL was given as student pointer, it is not added and
*			              False is returned.
*			              Method calls findPlace to find were the new student should
*			              be added. If no such place was found (returned value is NULL)
*		                  the method returns False. Otherwise, it calls internalAdd to
*			              add the new student to the list and returns true
***************************************************************************************/
bool StudentLinkedList::addNode(Student* newStudent) {

		if (newStudent == NULL) { // check if student given is valid data
			return false;
		}

		Node* before = findPlace(*newStudent); // find where, if any, student can be added

		// check if duplicate id was found
		if (before == NULL) {
			return false;
		}

		internalAdd(*newStudent, before); // add student
		return true;
}

/***************************************************************************************
* function name: incrmntCurrent()
* The Input: None
* The output: False if end is reached, True if not
* The Function operation: If current node is list's tail, False is returned because
*			              there is no next node to move to.
*		                  If current node is not list's tail, node is incremented.
*			              If now current node is list's tail, False is returned.
*		                  Otherwise, True is returned
***************************************************************************************/
bool StudentLinkedList::incrmntCurrent() {
	// already at list's end, return FALSE
	if (m_currentNode == m_tailDummy) {
		return false;
	}

	m_currentNode = m_currentNode->m_nextNode; // increment current node pointer to next node on list

	// reached on of list and return FALSE
	if (m_currentNode == m_tailDummy) {
		return false;
	}

	// has not reached end yet, return TRUE
	return true;
}

/***************************************************************************************
* function name: getCurrentID()
* The Input: None
* The output: Id of student which current node pointer points at.
* 	          If current node points at no student, ID_ERROR_VAL is returned
* The Function operation: Checks if student pointer is NULL. If so, returned ID_ERROR_VAL,
*                         otherwise uses the student's getID method in order to return
*                         the student's id
***************************************************************************************/
int StudentLinkedList::getCurrentID() const {
	// list is empty or reached end of list
	if (m_currentNode->m_studentData == NULL) {
		return ID_ERROR_VAL;
	}
	return m_currentNode->m_studentData->getID(); // return current Student's id
}

/***************************************************************************************
* function name: CurrentName()
* The Input: None
* The output: Name of student which current node pointer points at.
* 	          If current node points at no student, NAME_ERROR_VAL is returned
* The Function operation: Checks if student pointer is NULL. If so, returned NAME_ERROR_VAL,
*                         otherwise uses the student's getName method in order to return
*                         the student's name
***************************************************************************************/
string StudentLinkedList::getCurrentName() const {
	// list is empty or reached end of list
	if (m_currentNode->m_studentData == NULL) {
		return NAME_ERROR_VAL;
	}
	return m_currentNode->m_studentData->getStudentName(); // return current Student's name
}

/***************************************************************************************
* function name: searchByKey(const int key)
* The Input: Id to look for
* The output: Pointer to student whose id matches given value. If no such student
* 	          exists, NULL is returned.
* The Function operation: Initializes a node pointer to point at list's head. Method
*			              compares given value with the id of the student being
*                         pointed by current node. Scans stop once such student is found
*                         or list's end is reached. If a student is found, a pointer to
*                         the student is returned, otherwise NULL is returned
***************************************************************************************/
Student* StudentLinkedList::searchByKey(const int key) const {

	// scan list and find the student matching given key
	Node* currNode = m_headDummy->m_nextNode;
	while (currNode->m_studentData != NULL) {
		if (currNode->m_studentData->getID() == key) {
			return currNode->m_studentData;
		}
		currNode = currNode->m_nextNode; // move to next node
	}
	return NULL; // no match found
}

/***************************************************************************************
* function name: searchByIndex(const int index)
* The Input: Id to look for
* The output: Pointer to student whose index matches given value. If no such student
* 	          exists, NULL is returned.
* The Function operation: Initializes a node pointer to point at list's head. Method
*			              compares given value with the index of node pointed by current
*                         current node pointer. Scan stop once such student is found
*                         or list's end is reached. If a student is found, a pointer to
*                         the student is returned, otherwise NULL is returned
***************************************************************************************/
Student* StudentLinkedList::searchByIndex(const int index) const {

	// scan list and find the student whose node matching given index
	Node* currNode = m_headDummy;
	while (currNode != NULL) {
		if (currNode->m_index == index) {
			return currNode->m_studentData;
		}
		currNode = currNode->m_nextNode; // move to next node
	}
	return NULL; // no match found
}

/***************************************************************************************
* function name: setCurrentStudentPointer(Student* newStudent)
* The Input: New student for current node to point at
* The output: None
* The Function operation: Sets current node's student data pointer's index to given value
***************************************************************************************/
void StudentLinkedList::setCurrentStudentPointer(Student* newStudent) {
	m_currentNode->m_studentData = newStudent;
}
